package tool

import (
	"MoSkeleton/framework/utils"
	"MoSkeleton/models/tool"
	"MoSkeleton/services/base"
	"MoSkeleton/services/tool/logic"
	tool3 "MoSkeleton/web/pogos/request/tool"
	tool2 "MoSkeleton/web/pogos/response/tool"
	"errors"
	"gorm.io/gorm"
)

type GenTableColumnService struct {
	base.BaseService
}

func (g *GenTableColumnService) SaveColumnsByGenTables(tx *gorm.DB, dataSourceType string, genTables []*tool.GenTable, currentUsername string) (bool, error) {
	if tableCols, err := g.getSourceTableColumnsByTables(tx, dataSourceType, genTables, currentUsername); err != nil {
		return false, err
	} else {
		if len(tableCols) > 0 {
			result := g.GetDb(tx).Save(tableCols)
			return result.RowsAffected > 0, result.Error
		}
		return false, nil
	}
}

func (g *GenTableColumnService) SyncColumnsByGenTables(tx *gorm.DB, dataSourceType string, genTable *tool.GenTable, currentUsername string) (bool, error) {
	if tableCols, err := g.getSourceTableColumnsByTables(tx, dataSourceType, []*tool.GenTable{genTable}, currentUsername); err != nil {
		return false, err
	} else {
		tableIds := []uint64{genTable.ID}
		oldColumns, err := g.getGenTableColumnsByTableIds(&tableIds)
		if err != nil {
			return false, err
		}
		for _, column := range tableCols {
			oldTableCol := g.findGenTableColumnsByTableColumnId(oldColumns, column.TableId, column.ColumnName)
			if oldTableCol == nil {
				continue
			}
			oldTableCol.ColumnType = column.ColumnType
			oldTableCol.ColumnComment = column.ColumnComment
			oldTableCol.IsRequired = column.IsRequired
			oldTableCol.IsPk = column.IsPk
			oldTableCol.IsIncrement = column.IsIncrement
			oldTableCol.Sort = column.Sort
			oldTableCol.GoType = column.GoType
			oldTableCol.GoFieldName = column.GoFieldName
			oldTableCol.JsonFieldName = column.JsonFieldName
			oldTableCol.CreatedAt = nil
			oldTableCol.CreatedBy = ""
		}
		result := g.GetDb(tx).Save(oldColumns)
		if result.Error != nil {
			return false, result.Error
		}
		addItems, delItems, err := utils.DiffSliceByKey[*tool.GenTableColumn](oldColumns, tableCols, "ColumnName")
		if err != nil {
			return false, err
		}
		if len(addItems) > 0 {
			result = g.GetDb(tx).Save(addItems)
			if result.Error != nil {
				return false, result.Error
			}
		}
		if len(delItems) > 0 {
			result = g.GetDb(tx).Delete(delItems)
			if result.Error != nil {
				return false, result.Error
			}
		}
		return true, nil
	}
}

func (g *GenTableColumnService) UpdateColumnsByReq(tx *gorm.DB, itemsUpdateReq []*tool3.GenTableColumnUpdateReq, currentUsername string) (bool, error) {
	columns := make([]*tool.GenTableColumn, 0, len(itemsUpdateReq))
	for _, item := range itemsUpdateReq {
		item.UpdatedBy = currentUsername
		column := new(tool.GenTableColumn)
		err := utils.CopyStruct(column, item)
		if err != nil {
			return false, err
		}

		columns = append(columns, column)
	}
	if len(columns) > 0 {
		result := g.GetDb(tx).Omit("created_by,created_at,json_field_name").Save(columns)
		return result.RowsAffected > 0, result.Error
	}
	return false, nil
}

func (g *GenTableColumnService) DeleteColumnsByTableIds(tx *gorm.DB, tableIds *[]uint64) (bool, error) {
	result := g.GetDb(tx).Where("table_id in (?)", *tableIds).Delete(&tool.GenTableColumn{})
	return result.RowsAffected > 0, result.Error
}

func (g *GenTableColumnService) GetDetailItemsByTableId(tableId uint64) ([]*tool2.GenTableColumnItemResp, error) {
	itemsResp := make([]*tool2.GenTableColumnItemResp, 0)
	result := g.GetDb(nil).Table("gen_table_column").Where("table_id = ?", tableId).Order("sort asc").Find(&itemsResp)
	if result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return itemsResp, nil
	}
	return itemsResp, result.Error
}

func (g *GenTableColumnService) findGenTableColumnsByTableColumnId(tableColumns []*tool.GenTableColumn, tableId uint64, columnName string) *tool.GenTableColumn {
	for _, tableColumn := range tableColumns {
		if tableColumn.TableId == tableId && tableColumn.ColumnName == columnName {
			return tableColumn
		}
	}
	return nil
}
func (g *GenTableColumnService) getGenTableColumnsByTableIds(tableIds *[]uint64) ([]*tool.GenTableColumn, error) {
	tableCols := make([]*tool.GenTableColumn, 0)
	if err := g.GetDb(nil).Where("table_id in (?)", *tableIds).Find(&tableCols).Error; err != nil {
		return nil, err
	}
	return tableCols, nil
}

// select table_name, column_name, (case when (is_nullable = 'no' && column_key != 'PRI') then '1' else '0' end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type
//
//	from information_schema.columns where table_schema = (select database()) and table_name in ('sys_menu', 'sys_oper_log')
//	order by ordinal_position
func (g *GenTableColumnService) getSourceTableColumnsByTables(tx *gorm.DB, dataSourceType string, genTables []*tool.GenTable, currentUsername string) ([]*tool.GenTableColumn, error) {
	tableNames := g.tableNamesFromGenTable(genTables)
	tableColSchemes := make([]*tool.GenTableColumnScheme, 0)
	db := g.GetDb(tx).Select("table_name as table_name, column_name as column_name, (case when (is_nullable = 'no' && column_key != 'PRI') then '1' else '0' end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment as column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type as column_type")
	db = db.Table("information_schema.columns")
	db = db.Where("table_schema = (select database())  and table_name in (?)", tableNames)
	db.Order("ordinal_position asc")
	if err := db.Find(&tableColSchemes).Error; err != nil {
		return nil, err
	}
	tableCols := make([]*tool.GenTableColumn, 0)
	for _, tableColScheme := range tableColSchemes {
		tableCols = append(tableCols, &tool.GenTableColumn{
			TableId:       g.getTableIdByName(genTables, tableColScheme.TableName),
			ColumnComment: tableColScheme.ColumnComment,
			ColumnName:    tableColScheme.ColumnName,
			ColumnType:    tableColScheme.ColumnType,
			GoFieldName:   logic.ConvertToGoFieldName(tableColScheme.ColumnName),
			GoType:        logic.ConvertToGoType(dataSourceType, tableColScheme.ColumnType),
			JsonFieldName: logic.ConvertToJsonFieldName(tableColScheme.ColumnName),
			IsIncrement:   tableColScheme.IsIncrement,
			IsPk:          tableColScheme.IsPk,
			IsRequired:    tableColScheme.IsRequired,
			Sort:          tableColScheme.Sort,
			CreatedBy:     currentUsername,
			UpdatedBy:     currentUsername,
		})
	}
	return tableCols, nil
}

func (g *GenTableColumnService) getTableIdByName(genTables []*tool.GenTable, tableName string) uint64 {
	for _, genTable := range genTables {
		if genTable.SourceTableName == tableName {
			return genTable.MoModel.ID
		}
	}
	return 0
}

func (g *GenTableColumnService) tableNamesFromGenTable(genTables []*tool.GenTable) []string {
	tableNames := make([]string, 0)
	for _, genTable := range genTables {
		tableNames = append(tableNames, genTable.SourceTableName)
	}
	return tableNames
}
